package com.yby6.common.utils.upload;

import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;


/**
 * 阿里云OSS文件上传工具类
 *
 * @author TeouBle
 * @date 2021/06/11
 */
public class OSSFileUtil {

	/**
	 * 配置
	 */
	private static OSSConfig config = null;

	static {
		config = config == null ? new OSSConfig() : config;
	}

	/**
	 * 上传文件
	 *
	 * @param fileType 文件后缀 类型
	 * @param file     文件
	 * @return String 文件地址
	 * @MethodName: uploadFile
	 * @Description: OSS单文件上传
	 */
	public static String uploadFile(MultipartFile file, String fileType) {
		if (fileType == null){
			fileType = "jpg";
		}
		//文件名，根据UUID来
		String fileName = config.getPicLocation() + "yangbuyiya-" + UUID.randomUUID().toString().toUpperCase().replace("-", "") + "." + fileType;
		return putObject(file, fileType, fileName);
	}

	/**
	 * 下载文件
	 *
	 * @param name     的名字
	 * @param url      url
	 * @param response 响应
	 * @throws IOException ioexception
	 */
	public static void downloadFile(String name, String url, HttpServletResponse response) throws IOException {
		String newFileName = url;
		//第一步：设置响应类型 应用程序强制下载
		response.setContentType("application/force-download");
		//第二读取文件
		OSS ossClient = new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
		OSSObject ossObj = ossClient.getObject(OSSFileUtil.getBucketName(url), url.replace(config.getOssUrl(), ""));
		InputStream objectContent = ossObj.getObjectContent();
		//设置响应头，对文件进行url编码
		name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
		response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", name + newFileName.substring(newFileName.lastIndexOf("."))));
		response.setCharacterEncoding("UTF-8");
		//第三步：老套路，开始copy
		OutputStream out = response.getOutputStream();
		byte[] b = new byte[1024];
		int len = 0;
		while ((len = objectContent.read(b)) != -1) {
			out.write(b, 0, len);
		}
		out.flush();
		out.close();
		objectContent.close();
		// 关闭OSSClient。
		ossClient.shutdown();
	}

	/**
	 * 更新文件
	 *
	 * @param file     文件
	 * @param fileType 文件类型
	 * @param oldUrl   旧的url
	 * @return String
	 * @MethodName: updateFile
	 * @Description: 更新文件:只更新内容，不更新文件名和文件地址。
	 * (因为地址没变，可能存在浏览器原数据缓存，不能及时加载新数据，例如图片更新，请注意)
	 */
	public static String updateFile(MultipartFile file, String fileType, String oldUrl) {
		String fileName = getFileName(oldUrl);
		if (fileName == null) {
			return null;
		}
		return putObject(file, fileType, fileName);
	}

	/**
	 * 替换文件
	 *
	 * @param fileType 文件后缀
	 * @param oldUrl   需要删除的文件地址
	 * @param file     文件
	 * @return String 文件地址
	 * @MethodName: replaceFile
	 * @Description: 替换文件:删除原文件并上传新文件，文件名和地址同时替换
	 * 解决原数据缓存问题，只要更新了地址，就能重新加载数据)
	 */
	public static String replaceFile(MultipartFile file, String fileType, String oldUrl) {
		// 先删除原文件
		boolean flag = deleteFile(oldUrl);
		if (!flag) {
			//更改文件的过期时间，让他到期自动删除。
		}
		return uploadFile(file, fileType);
	}

	/**
	 * 删除文件
	 *
	 * @param fileUrl 需要删除的文件url
	 * @return boolean 是否删除成功
	 * @MethodName: deleteFile
	 * @Description: 单文件删除
	 */
	public static boolean deleteFile(String fileUrl) {
		config = config == null ? new OSSConfig() : config;
		// 根据url获取bucketName
		String bucketName = OSSFileUtil.getBucketName(fileUrl);
		// 根据url获取fileName
		String fileName = OSSFileUtil.getFileName(fileUrl);
		if (bucketName == null || fileName == null) {
			return false;
		}
		OSSClient ossClient = null;
		try {
			ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
			GenericRequest request = new DeleteObjectsRequest(bucketName).withKey(fileName);
			ossClient.deleteObject(request);
		} catch (Exception oe) {
			oe.printStackTrace();
			return false;
		} finally {
			ossClient.shutdown();
		}
		return true;
	}

	/**
	 * 删除文件
	 *
	 * @param fileUrls 需要删除的文件url集合
	 * @return int 成功删除的个数
	 * @MethodName: batchDeleteFiles
	 * @Description: 批量文件删除(较快)：适用于相同endPoint和BucketName
	 */
	public static int deleteFile(List<String> fileUrls) {
		int deleteCount = 0;    //成功删除的个数
		String bucketName = OSSFileUtil.getBucketName(fileUrls.get(0));       //根据url获取bucketName
		List<String> fileNames = OSSFileUtil.getFileName(fileUrls);         //根据url获取fileName
		if (bucketName == null || fileNames.size() <= 0) {
			return 0;
		}
		OSSClient ossClient = null;
		try {
			ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
			DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName).withKeys(fileNames);
			DeleteObjectsResult result = ossClient.deleteObjects(request);
			deleteCount = result.getDeletedObjects().size();
		} catch (OSSException oe) {
			oe.printStackTrace();
			throw new RuntimeException("OSS服务异常:", oe);
		} catch (ClientException ce) {
			ce.printStackTrace();
			throw new RuntimeException("OSS客户端异常:", ce);
		} finally {
			ossClient.shutdown();
		}
		return deleteCount;

	}

	/**
	 * 删除文件
	 *
	 * @param fileUrls 需要删除的文件url集合
	 * @return int 成功删除的个数
	 * @MethodName: batchDeleteFiles
	 * @Description: 批量文件删除(较慢)：适用于不同endPoint和BucketName
	 */
	public static int deleteFiles(List<String> fileUrls) {
		int count = 0;
		for (String url : fileUrls) {
			if (deleteFile(url)) {
				count++;
			}
		}
		return count;
	}

	/**
	 * 把对象
	 *
	 * @param file     文件
	 * @param fileType 文件类型
	 * @param fileName 文件名称
	 * @return String
	 * @MethodName: putObject
	 * @Description: 上传文件
	 */
	public static String putObject(MultipartFile file, String fileType, String fileName) {
		config = config == null ? new OSSConfig() : config;
		String url = null;      //默认null
		OSSClient ossClient = null;
		try {
			ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
			InputStream inputStream = file.getInputStream();
			ObjectMetadata meta = new ObjectMetadata();             // 创建上传Object的Metadata
			meta.setContentType(OSSFileUtil.contentType(fileType));       // 设置上传内容类型
			meta.setCacheControl("no-cache");                   // 被下载时网页的缓存行为
			PutObjectRequest request = new PutObjectRequest(config.getBucketName(), fileName, inputStream, meta);           //创建上传请求
			ossClient.putObject(request);
			//上传成功再返回的文件路径
			url = config.getOssUrl() + fileName;
		} catch (OSSException oe) {
			oe.printStackTrace();
			return null;
		} catch (ClientException ce) {
			ce.printStackTrace();
			return null;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			ossClient.shutdown();
		}
		return url;
	}

	/**
	 * 内容类型
	 *
	 * @param fileType 文件类型
	 * @return String
	 * @MethodName: contentType
	 * @Description: 获取文件类型
	 */
	private static String contentType(String fileType) {
		fileType = fileType.toLowerCase();
		String contentType = "";
		switch (fileType) {
			case "bmp":
				contentType = "image/bmp";
				break;
			case "gif":
				contentType = "image/gif";
				break;
			case "png":
			case "jpeg":
			case "jpg":
				contentType = "image/jpeg";
				break;
			case "html":
				contentType = "text/html";
				break;
			case "txt":
				contentType = "text/plain";
				break;
			case "vsd":
				contentType = "application/vnd.visio";
				break;
			case "ppt":
			case "pptx":
				contentType = "application/vnd.ms-powerpoint";
				break;
			case "doc":
			case "docx":
				contentType = "application/msword";
				break;
			case "xml":
				contentType = "text/xml";
				break;
			case "mp4":
				contentType = "video/mp4";
				break;
			default:
				contentType = "application/octet-stream";
				break;
		}
		return contentType;
	}

	/**
	 * 桶名字
	 *
	 * @param fileUrl 文件url
	 * @return String bucketName
	 * @MethodName: getBucketName
	 * @Description: 根据url获取bucketName
	 */
	private static String getBucketName(String fileUrl) {
		int startIndex = fileUrl.indexOf("/");
		int endIndex = fileUrl.indexOf(".");
		return fileUrl.substring(startIndex, endIndex).replace("//", "");
	}

	/**
	 * 获取文件名称
	 *
	 * @param fileUrl 文件url
	 * @return String fileName
	 * @MethodName: getFileName
	 * @Description: 根据url获取fileName
	 */
	private static String getFileName(String fileUrl) {
		String str = "yangbuyi.top/";
		int beginIndex = fileUrl.indexOf(str);
		if (beginIndex == -1) {
			return null;
		}
		return fileUrl.substring(beginIndex + str.length());
	}

	/**
	 * 获取文件名称
	 *
	 * @param fileUrls 文件的url
	 * @return List<String>  fileName集合
	 * @MethodName: getFileName
	 * @Description: 根据url获取fileNames集合
	 */
	private static List<String> getFileName(List<String> fileUrls) {
		List<String> names = new ArrayList<>();
		for (String url : fileUrls) {
			names.add(getFileName(url));
		}
		return names;
	}
}